/**
 * Created by Administrator on 2015/12/7.
 */
var fs = require('fs');
function replace(template,obj){
    var pattern = /{([_\w\.]+)}/g;
    return template.replace(pattern,function(match,p1,offset,string){
        var arr = p1.split('.');
        if(arr.length > 1){
            var i = 0;
            var len = arr.length;
            var subObj = obj[arr[0]];
            while(typeof subObj === 'object' && i < len){
                i = i + 1;
                subObj = subObj[arr[i]];
            }
            if(i === len-1){
                if(subObj === undefined){
                    return "";
                }else{
                    return subObj;
                }
            }else {
                return "";
            }
        }else{
            var ret = obj[p1];
            if(ret === undefined)
            {
                return "";
            }else{
                return ret;
            }
        }

    })
}
function render(filePath,options,callback){
    fs.readFile(filePath, function (err, content) {
        if (err) return callback(new Error(err));
        var rendered = replace4(content.toString(),options);
        return callback(null, rendered);
    })
}

/**
 * 第二版render2 支持include指令
 * @param filePath
 * @param options
 * @param callback
 */
function render2(filePath,options,callback){
    fs.readFile(filePath, function (err, content) {
        if (err) return callback(new Error(err));
        replace5(content.toString(),options,function(ret){
            callback(null,ret);
        });
    })
}
/*模板引擎测试
所有的字段使用{xx} 或者{xx.xx}
如果模板中想要获取的字段不存在，那么模板引擎会将此次替换为空字符串
var article =
{
    _id:'23465465434132132',
    title:'This is a test',
    body:'sdfkasjdfl;kasjdflkas;jdf;aslkdf',
              user:{
                  _id:'546546546546',
                  username:'dongge'
              },
    date:new Date()
}
render('./Hello.html',article,function(err,r){
    console.log(r);
});*/
exports.__express = render2;

/*********************************/

function getValue(name,options){
    if(!name)
    {
        return undefined;
    }
    var arr = name.split('.');
    options = options || {};
    if(arr.length > 1){
        var i = 0;
        var len = arr.length;
        var subObj = options;
        while(i < len && typeof subObj === 'object'){
            subObj = subObj[arr[i]];
            i = i + 1;
        }
        if(i === len){
            return subObj;
        }else {
            return undefined;
        }
    }else{
        return options[name];
    }
}
var keys ={
    "undefined":true,
    "null":true
}
function addThis(str){
    var pattern = /((["']?)([A-Za-z_]\w*)\2)/g;
    return str.replace(pattern,function (match,p1,p2,p3){
        if(p2 === '"' || p2 === "'"){
            return p1;
        }
        else{
            if(keys[p3]){
                return p3;
            }
            return "this."+p3;
        }
    })
}

function replace3(template,options){
    var pattern =/<(iterator|property|if)\s*(\w+\s*=\s*\{[^}]*})>(.*?)<\/\1>/g;
    return template.replace(pattern,function(match,p1,p2,p3){
        if(p1 === 'iterator'){
            var pName = getProperty('value',p2);
            var value = getValue(pName,options);
            //console.log(pName);
            value = value || {};
            var retArr = [];
            for(var i in value){
                var loptions = value[i];
                if(typeof loptions!=='object'){
                    loptions = {top:value[i]};
                }else{
                    loptions.top = value[i];
                }
                var ret = replace3(p3,loptions);
                retArr.push(ret);
            }
            return retArr.join('');
        }
        if(p1 === 'property'){
            var value = getValue(getProperty('value',p2),options);
            //console.log(value);
            return value||'';
        }
        if(p1 === 'if'){
            var testStr = getProperty('test',p2);
            testStr = addThis(testStr);
            var testFun = new Function('return '+ testStr);
            if(testFun.call(options) === true){
                return replace3(p3,options);
            }else{
                return '';
            }
        }
    })
}
function getProperty(name,str){
    //var regex = new RegExp(''+ name+'={([\\w\\.]+)}');
    var regex = new RegExp(''+ name+'={([^}]+)}');
    var match = regex.exec(str);
    if(match === null){
        return undefined;
    }
    return match[1];
}
function replace4(template,options){
    //var pattern =/<(iterator|property|if)\s*([\w\.}"{=]+)>(.*)<\/\1>/g;if cannot work
    //var pattern =/<(iterator|property|if)\s+(.*)>(.*)<\/\1>/g;iterator cannot work
    //var pattern =/<(iterator|property|if)([^>]*)>(.*)<\/\1>/g;
    //var pattern =/<(iterator|property|if)\s*(\w+\s*=\s*\{[^}]*})>(.*)<\/\1>/g;
    var pattern =/<(iterator|property|if)\s*(\w+\s*=\s*\{[^}]*})>([^]*?)<\/\1>/g;//采用非贪婪匹配
    return template.replace(pattern,function(match,p1,p2,p3,offset,string){
        if(p1 === 'iterator'){
            var pName = getProperty('value',p2);
            //var value = getValue(pName,options);
            var value = calcExp(options,pName);
            value = value || {};
            var retArr = [];
            for(var i in value){
                var loptions = {};
                var v = value[i];
                loptions.top = v;
                if(typeof  v === 'object'){
                    Object.keys(v).forEach(function(prop){
                        loptions[prop] = v[prop];
                    })
                }
                /**
                 * 迭代之后必须更新option给下面的模板
                 * 这里额外添加一个top属性，代表本对象
                 * 如果迭代值也是个对象就把其中的属性复制到option中
                 * 存在问题：
                 * 如果在循环中要获取不在迭代中的值，就无法获取
                 * 要能沿着作用域向上搜索
                 */
                var ret = replace4(p3,loptions);
                retArr.push(ret);
            }
            return retArr.join('');
        }
        if(p1 === 'property'){
            //var value = getValue(getProperty('value',p2),options);
            var value = calcExp(options,getProperty('value',p2));
            //console.log(value);
            if(value === null || value === undefined){
                return '';
            }else{
                return value;
            }
            //return value||''; 如果value=0，那么value||''等于空，这显然不对 2016.1.16
        }
        /**
         * 这里的修改剔除了添加this的功能
         * 给变量添加this其实就是为了获取
         * options中的变量，这种方法有很多不合理之处，实际上还需要对整个表达式进行解析
         * 包括过滤掉关键字等问题
         * 这里使用eval(var...)
         * 来动态增加闭包中的变量，然后通过eval来计算
         * 表达式的值，这种方法比较合理，使用动态函数来计算表达式的值不好控制闭包问题
         */
        if(p1 === 'if'){
            var testStr = getProperty('test',p2);
            //testStr = addThis(testStr);
            //var testFun = new Function('return '+ testStr);

            //if(testFun.call(options) === true){
            if(calcExp(options,testStr)){
                return replace4(p3,options);
            }else{
                return '';
            }
        }
    })
}

function replace4_1(template,options){
    //var pattern =/<(iterator|property|if)\s*([\w\.}"{=]+)>(.*)<\/\1>/g;if cannot work
    //var pattern =/<(iterator|property|if)\s+(.*)>(.*)<\/\1>/g;iterator cannot work
    //var pattern =/<(iterator|property|if)([^>]*)>(.*)<\/\1>/g;
    //var pattern =/<(iterator|property|if)\s*(\w+\s*=\s*\{[^}]*})>(.*)<\/\1>/g;
    var pattern =/<(iterator|property|if)\s*(\w+\s*=\s*\{[^}]*})>(.*?)<\/\1>/g;//采用非贪婪匹配
    return template.replace(pattern,function(match,p1,p2,p3,offset,string){
        if(p1 === 'iterator'){
            var pName = getProperty('value',p2);
            //var value = getValue(pName,options);
            var value = calcExp(options,pName);
            value = value || {};
            var retArr = [];
            for(var i in value){
                var loptions = value[i];
                if(typeof loptions!=='object'){
                    loptions = {top:value[i]};
                }else{
                    loptions.top = value[i];
                }
                var ret = replace3(p3,loptions);
                retArr.push(ret);
            }
            return retArr.join('');
        }
        if(p1 === 'property'){
            //var value = getValue(getProperty('value',p2),options);
            var value = calcExp(options,getProperty('value',p2));
            //console.log(value);
            if(value === null || value === undefined){
                return '';
            }else{
              return value;
            }
            //return value||''; 如果value=0，那么value||''等于空，这显然不对 2016.1.16
        }
        /**
         * 这里的修改剔除了添加this的功能
         * 给变量添加this其实就是为了获取
         * options中的变量，这种方法有很多不合理之处，实际上还需要对整个表达式进行解析
         * 包括过滤掉关键字等问题
         * 这里使用eval(var...)
         * 来动态增加闭包中的变量，然后通过eval来计算
         * 表达式的值，这种方法比较合理，使用动态函数来计算表达式的值不好控制闭包问题
         */
        if(p1 === 'if'){
            var testStr = getProperty('test',p2);
            //testStr = addThis(testStr);
            //var testFun = new Function('return '+ testStr);

            //if(testFun.call(options) === true){
            if(calcExp(options,testStr)){
                return replace4(p3,options);
            }else{
                return '';
            }
        }
    })
}
/**
 * callback = function(str){...}
 * @param template
 * @param options
 * @param callback
 */
function handleInclude(template,options,callback){
    var pattern =/<include\s*(\w+\s*=\s*\{([^}]*)})>(.*?)<\/include>/g;
    var match = null;
    var arr = [];
    var lastIndex = 0;
    while(match = pattern.exec(template)){
        if(lastIndex === match.index){
            //arr.push(readFile(calcExp(options,JSON.stringify(match[2]))));//calcExp(options,pName);
            arr.push(readFile(calcExp(options,match[2])));
        }else {
            arr.push(template.slice(lastIndex,match.index));
            //arr.push(readFile(calcExp(options,JSON.stringify(match[2]))));
            arr.push(readFile(calcExp(options,match[2])));
        }
        lastIndex = pattern.lastIndex;
        //console.log(match);
    }
    if(lastIndex != template.length){//没有匹配到字符串最后
        arr.push(template.slice(lastIndex,template.length));
    }
    function readFile(name){
        return function(callback){
            fs.readFile(name,callback);
        }
    }
    function f(i){
        if(i === arr.length){
            if(callback){
                callback(arr.join(''));
            }
            return;
        }
        var fun = arr[i];
        if(typeof  fun == 'function'){
            fun(function(err,data){
                if(err){
                    arr[i] = '';
                }else {
                    arr[i] = data.toString();
                }
                f(i+1);
            });
        }else {
            f(i+1);
        }
    }
    f(0);
}
/**
 * 第五版的replace增加include指令,include指令要读取文件（异步调用）
 * 先处理include指令，后面再处理property iterator if
 * @param template
 * @param options
 * @param callback
 * @returns {*}
 */
function replace5(template,options,callback){
    handleInclude(template,options,function(result){
        var ret = replace4(result,options);
        if(callback){
            callback(ret);
        }
    });
}
/**
 * 如果出现异常将会返回undefined
 * options必须要为对象才行
 * @param options
 * @param exp
 * @returns
 */
function calcExp(options,exp){
    if(typeof  options ==='object'){
        for(var i in options){
            eval('var '+ i + '=' + JSON.stringify(options[i]));
        }
    }
    try{
        var ret = eval(exp);
    }catch (err){
        ret = undefined
    }
    return ret;
}
/**
 * 属性值的格式是name={value}
 * @param name
 * @param str
 * @returns value
 */